﻿#include "UrlEncoder.h"

static inline unsigned char ToHex(unsigned char x)
{
    return  x > 9 ? x + 55 : x + 48;
}

int UrlEncode(const void *in, size_t insz, std::string & outstr)
{
    outstr.resize(insz * 3);
    size_t outsz = 0;
    unsigned char *str = (unsigned char *)in;
    char *strTemp = (char *)outstr.data();

    for (size_t i = 0; i < insz; i++, outsz){
        if (isalnum(str[i]) ||
            (str[i] == '-') ||
            (str[i] == '_') ||
            (str[i] == '.') ||
            (str[i] == '~')){
                strTemp[outsz++] = str[i];
        } else if (str[i] == ' '){
            strTemp[outsz++] = '+';
        } else {
            strTemp[outsz++] = '%';
            strTemp[outsz++] = ToHex(str[i] >> 4);
            strTemp[outsz++] = ToHex(str[i] % 16);
        }
    }

    outstr.resize(outsz);
    return 0;
}

/*
 * URL decode insz bytes from in.
 * ok:return 0
 * If in does not contain valid URL encoded data, returns -1.
 * come from sslsplit
 */
int UrlDecode(const void *parm, size_t insz, std::string& outstr)
{
    const char *in = (const char *)parm;

    static const int hex2dec[] = {
        -1, -1, -1, -1, -1, -1, -1, -1,   /*   0 ..   7 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*   8 ..  15 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*  16 ..  23 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*  24 ..  31 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*  32 ..  39 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*  40 ..  47 */
        0,  1,  2,  3,  4,  5,  6,  7,   /*  48  .. 55 */
        8,  9, -1, -1, -1, -1, -1, -1,   /*  56 ..  63 */
        -1, 10, 11, 12, 13, 14, 15, -1,   /*  64 ..  71 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*  72 ..  79 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*  80 ..  87 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /*  88 ..  95 */
        -1, 10, 11, 12, 13, 14, 15, -1,   /*  96 .. 103 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 104 .. 111 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 112 .. 119 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 120 .. 127 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 128 .. 135 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 136 .. 143 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 144 .. 151 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 152 .. 159 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 160 .. 167 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 168 .. 175 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 176 .. 183 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 184 .. 191 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 192 .. 199 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 200 .. 207 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 208 .. 215 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 216 .. 223 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 224 .. 231 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 232 .. 239 */
        -1, -1, -1, -1, -1, -1, -1, -1,   /* 240 .. 247 */
        -1, -1, -1, -1, -1, -1, -1, -1 }; /* 248 .. 255 */

	size_t i, o;
	int hi, lo;
	char *out;

	if (insz == 0) {
		outstr.clear();
		return 0;
	}

	for (i = 0, o = 0; i < insz; i++)
		if (in[i] == '%')
			o++;
	if (2 * o > insz) {
		outstr.clear();
		return -1;
	}
    outstr.resize(insz - (2 * o));
    out = (char *)outstr.c_str();

	for (i = 0, o = 0; i < insz; i++, o++) {
		if (in[i] != '%') {
			out[o] = in[i];
			continue;
		}
		if (i + 2 >= insz)
			goto leave;
		if ((hi = hex2dec[(unsigned char)in[i + 1]]) == -1)
			goto leave;
		if ((lo = hex2dec[(unsigned char)in[i + 2]]) == -1)
			goto leave;
		out[o] = ((hi & 0xF) << 4) | (lo & 0xF);
		i += 2;
	}
	return 0;

leave:
	outstr.clear();
	return -1;
}

#if _DEBUG
using namespace std;
#include <windows.h>
#include <stdio.h>
#include <assert.h>
void testurlencoder()
{
#define NUM 10000
    string ori = "假定前面你都看懂了，那么此时你应该会感到很头痛。因为，实在太混乱了。不同的操作系统、不同的浏览器、不同的网页字符集，将导致完全不同的编码结果。如果程序员要把每一种结果都考虑进去，是不是太恐怖了？有没有办法，能够保证客户端只用一种编码方法向服务器发出请求？";

    string e1;

    UrlEncode(ori.c_str(), ori.size(), e1);
    size_t i = 0;

    unsigned long t2 = GetTickCount();

   // for(i = 0; i < NUM; i++){
            string d2;
        UrlDecode(e1.c_str(), e1.size(), d2);
        assert(d2 == ori);

    //}
    unsigned long t3 = GetTickCount();
    printf(" %d",  t3-t2);


}

#endif